package ru.ssau.customerManagement.controller.remoting.notifications;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.ssau.customerManagement.controller.remoting.OperationRequest;
import ru.ssau.customerManagement.controller.remoting.OperationResponse;
import ru.ssau.customerManagement.model.entities.Customer;
import ru.ssau.customerManagement.model.entities.CustomerLocation;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by illabefat on 24.11.14.
 */
public class ServerSideNotificationController implements NotificationController{
    private static final Logger logger = LoggerFactory.getLogger(ServerSideNotificationController.class);

    private List<Socket> listeners;

    public ServerSideNotificationController() {
        listeners = new CopyOnWriteArrayList<Socket>();
    }

    @Override
    public void customerChanged(Customer customer) {
        notifyListeners(new OperationRequest("customerChanged", customer));
    }

    @Override
    public void customerAdded(Customer customer) {
        notifyListeners(new OperationRequest("customerAdded", customer));
    }

    @Override
    public void customerRemoved(Long id) {
        notifyListeners(new OperationRequest("customerRemoved", id));
    }

    @Override
    public void customerLocationChanged(CustomerLocation location) {
        notifyListeners(new OperationRequest("customerLocationChanged", location));
    }

    @Override
    public void customerLocationAdded(CustomerLocation location) {
        notifyListeners(new OperationRequest("customerLocationAdded", location));
    }

    @Override
    public void customerLocationRemoved(Long id) {
        notifyListeners(new OperationRequest("customerLocationRemoved", id));
    }

    private synchronized void notifyListeners(OperationRequest request) {
        List<Socket> itemsToDelete = null;
        for (Socket soc : listeners)
        {
            try
            {
                ObjectOutputStream out = new ObjectOutputStream(soc.getOutputStream());
                ObjectInputStream in = new ObjectInputStream(soc.getInputStream());

                JAXBContext jaxbContext1 = JAXBContext.newInstance(OperationResponse.class, OperationRequest.class,
                        Customer.class, CustomerLocation.class);
                Marshaller jaxbMarshaller = jaxbContext1.createMarshaller();
                Unmarshaller jaxbUnmarshaller = jaxbContext1.createUnmarshaller();

                StringWriter sw = new StringWriter();
                jaxbMarshaller.marshal(request, sw);
                String xmlString = sw.toString();

                out.writeUTF(xmlString);
                out.flush();
                out.reset();

                String xmlRequest = in.readUTF();
                OperationResponse res = (OperationResponse) jaxbUnmarshaller.unmarshal(new StringReader(xmlRequest));
            } catch (SocketException e) {
                logger.info("Connection closed");
                if (itemsToDelete == null)
                {
                    itemsToDelete = new ArrayList<Socket>(Arrays.asList(soc));
                }
                else
                {
                    itemsToDelete.add(soc);
                }
            } catch (IOException e) {
                logger.error("Some error in notification", e);
            } catch (JAXBException e) {
                logger.error("Some error in notification", e);
            }
        }

        if (itemsToDelete != null)
        {
            listeners.removeAll(itemsToDelete);
        }
    }

    public void addListener(Socket soc)
    {
        listeners.add(soc);
    }
}
